#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include <sys/stat.h>

#define MAX_CMD 1024
#define OPT_NUM 64
#define trimSpace(start) do {\
    while(isspace(*start)) start++;\
  }while(0)


char lineCommand[MAX_CMD];
char *myargv[OPT_NUM]; // 指针数组
int lastCode  = 0;
int lastSig = 0;


#define NONE_REDIR    0
#define INPUT_REDIR   1
#define OUTPUT_REDIR  2
#define APPEND_REDIR  3

int fileType = NONE_REDIR;
char *redirFile = NULL;


// 切割字符串
// ls -a -l > myfile.txt  ---> "ls -a -l"  "myfile.txt"      
void commandCheck(char *commands) {
  
  assert(commands);
  char *start = commands;
  char *end = commands + strlen(commands);

  while(start < end) {
    if(*start == '>') {
      *start = '\0';
      start++;
      if(*start == '>') {
        fileType = APPEND_REDIR;
        start++;
      }else{
        fileType = OUTPUT_REDIR;  
      }
      // 再将 > 后面的空格进行去除
      trimSpace(start);
      redirFile = start;
      break;
    }else if (*start == '<') {
      *start = '\0';
      start++;
      trimSpace(start);
      fileType = INPUT_REDIR;
      redirFile = start;
      break;

    }else {
      start++;
    }
  }
}


int main() {


  while(1) {
    fileType = NONE_REDIR;
    redirFile = NULL;
   int errno = 0;

  // 提示用户进行输入命令
  printf("用户名@主机名 当前路径# ");
  fflush(stdout);
  //sleep(10);
  // 让用户输入
  
  char *msg = fgets(lineCommand,sizeof(lineCommand)-1,stdin);
  assert(msg != NULL);
  (void)msg;
  // 将输入的字符串的最后一位置为0 ，因为我们在输入的时候最后都会输入一个回车键 也就是\n
  lineCommand[strlen(lineCommand) -1 ] = 0;
 // printf("commend :  %s",lineCommand);

  // 进行命令行重定向的操作 例如： 
  // "ls -a -l > myfile.txt" ---> "ls -a -l"  "myfile.txt" ，定义一个函数，用来切割两个字符串
  commandCheck(lineCommand);

  // 将输入的命令进行切割 因为输入的命令 可能为 "ls -a -l"  切割后为 "ls" "-a" "-l"
  // 字符串切割，定义一个指针数组，数组里的每一个指针指向每一个字符串的地址
  myargv[0] = strtok(lineCommand," ");
  int i = 1;
  // 如果指令为ls 就将ls的后面添加color颜色
  if(myargv[0] != NULL && strcmp(myargv[0],"ls") == 0) {
    myargv[i++] = (char*)"--color=auto";
  }



  while(myargv[i++] = strtok(NULL," "));

  // 如果指令为cd ，这时执行该指令不需要创建子进程，让shell自己执行对应的命令
  if(myargv[0] != NULL && strcmp(myargv[0],"cd")== 0) {
    if(myargv[1] != NULL) {
      chdir(myargv[1]);
    }
    continue;
  }

  // 如果指令为echo $?
  if(myargv[0] !=NULL && myargv[1] != NULL && strcmp(myargv[0],"echo")==0) {
    if(strcmp(myargv[1],"$?")==0){
      printf("%d, %d\n",lastCode,lastSig);
    }else{
      printf("%s\n",myargv[1]);
    }
    continue;
  }

  // 测试一下切割的是否正确  
//#ifdef DEBUG
//  for(int i = 0; myargv[i];i++) {
//    printf("myargv[%d] = %s\n",i,myargv[i]);
//  }
//
//#endif

  // 执行命令
  pid_t id = fork();
  assert(id != -1);

  // 让子进程执行命令
  if(id == 0) {

    // 执行重定向指令
    // 因为命令是子进程执行的，所以重定向的工作一定是子进程来完成
    
    switch(fileType) {
      case NONE_REDIR:
        // 没有重定向的指令，什么都不做
        break;
      case INPUT_REDIR:
        {
        int fd = open(redirFile,O_RDONLY);
        if(fd < 0) {
          perror("open");
          exit(errno);
        }
        // 重定向读文件
        dup2(fd,0);
        }
        break;
      case OUTPUT_REDIR:
      case APPEND_REDIR:
        {
          umask(0);
          int flags = O_WRONLY | O_CREAT;
          if(fileType == APPEND_REDIR) flags |= O_APPEND;
          else{
            flags |= O_TRUNC;
          }

          int fd = open(redirFile,flags,0666);
          if(fd < 0) {
            perror("open");
            exit(errno);
          }
          dup2(fd,1);
        }
        break;
      default:
        printf("出现bug，请检查后重试\n");
        exit(1);
    }

    execvp(myargv[0],myargv);
    exit(1);
  }

  int status = 0;

  waitpid(id,&status,0);
  lastCode = ((status >> 8) & 0xFF);
  lastSig = (status & 0x7F);

  }
  //return 0;
}

